
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

/**
 * AUTO-GENERATED FILE. DO NOT MODIFY.
 */

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
import { makeInner } from '../../util/model.js';
import * as modelHelper from './modelHelper.js';
import findPointFromSeries from './findPointFromSeries.js';
import { each, curry, bind, extend } from 'zrender/lib/core/util.js';
var inner = makeInner();
/**
 * Basic logic: check all axis, if they do not demand show/highlight,
 * then hide/downplay them.
 *
 * @return content of event obj for echarts.connect.
 */

export default function axisTrigger(payload, ecModel, api) {
	var currTrigger = payload.currTrigger;
	var point = [payload.x, payload.y];
	var finder = payload;
	var dispatchAction = payload.dispatchAction || bind(api.dispatchAction, api);
	var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo; // Pending
	// See #6121. But we are not able to reproduce it yet.

	if (!coordSysAxesInfo) {
		return;
	}

	if (illegalPoint(point)) {
		// Used in the default behavior of `connection`: use the sample seriesIndex
		// and dataIndex. And also used in the tooltipView trigger.
		point = findPointFromSeries({
			seriesIndex: finder.seriesIndex,
			// Do not use dataIndexInside from other ec instance.
			// FIXME: auto detect it?
			dataIndex: finder.dataIndex
		}, ecModel).point;
	}

	var isIllegalPoint = illegalPoint(point); // Axis and value can be specified when calling dispatchAction({type: 'updateAxisPointer'}).
	// Notice: In this case, it is difficult to get the `point` (which is necessary to show
	// tooltip, so if point is not given, we just use the point found by sample seriesIndex
	// and dataIndex.

	var inputAxesInfo = finder.axesInfo;
	var axesInfo = coordSysAxesInfo.axesInfo;
	var shouldHide = currTrigger === 'leave' || illegalPoint(point);
	var outputPayload = {};
	var showValueMap = {};
	var dataByCoordSys = {
		list: [],
		map: {}
	};
	var updaters = {
		showPointer: curry(showPointer, showValueMap),
		showTooltip: curry(showTooltip, dataByCoordSys)
	}; // Process for triggered axes.

	each(coordSysAxesInfo.coordSysMap, function (coordSys, coordSysKey) {
		// If a point given, it must be contained by the coordinate system.
		var coordSysContainsPoint = isIllegalPoint || coordSys.containPoint(point);
		each(coordSysAxesInfo.coordSysAxesInfo[coordSysKey], function (axisInfo, key) {
			var axis = axisInfo.axis;
			var inputAxisInfo = findInputAxisInfo(inputAxesInfo, axisInfo); // If no inputAxesInfo, no axis is restricted.

			if (!shouldHide && coordSysContainsPoint && (!inputAxesInfo || inputAxisInfo)) {
				var val = inputAxisInfo && inputAxisInfo.value;

				if (val == null && !isIllegalPoint) {
					val = axis.pointToData(point);
				}

				val != null && processOnAxis(axisInfo, val, updaters, false, outputPayload);
			}
		});
	}); // Process for linked axes.

	var linkTriggers = {};
	each(axesInfo, function (tarAxisInfo, tarKey) {
		var linkGroup = tarAxisInfo.linkGroup; // If axis has been triggered in the previous stage, it should not be triggered by link.

		if (linkGroup && !showValueMap[tarKey]) {
			each(linkGroup.axesInfo, function (srcAxisInfo, srcKey) {
				var srcValItem = showValueMap[srcKey]; // If srcValItem exist, source axis is triggered, so link to target axis.

				if (srcAxisInfo !== tarAxisInfo && srcValItem) {
					var val = srcValItem.value;
					linkGroup.mapper && (val = tarAxisInfo.axis.scale.parse(linkGroup.mapper(val, makeMapperParam(srcAxisInfo), makeMapperParam(tarAxisInfo))));
					linkTriggers[tarAxisInfo.key] = val;
				}
			});
		}
	});
	each(linkTriggers, function (val, tarKey) {
		processOnAxis(axesInfo[tarKey], val, updaters, true, outputPayload);
	});
	updateModelActually(showValueMap, axesInfo, outputPayload);
	dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction);
	dispatchHighDownActually(axesInfo, dispatchAction, api);
	return outputPayload;
}

function processOnAxis(axisInfo, newValue, updaters, noSnap, outputFinder) {
	var axis = axisInfo.axis;

	if (axis.scale.isBlank() || !axis.containData(newValue)) {
		return;
	}

	if (!axisInfo.involveSeries) {
		updaters.showPointer(axisInfo, newValue);
		return;
	} // Heavy calculation. So put it after axis.containData checking.

	var payloadInfo = buildPayloadsBySeries(newValue, axisInfo);
	var payloadBatch = payloadInfo.payloadBatch;
	var snapToValue = payloadInfo.snapToValue; // Fill content of event obj for echarts.connect.
	// By default use the first involved series data as a sample to connect.

	if (payloadBatch[0] && outputFinder.seriesIndex == null) {
		extend(outputFinder, payloadBatch[0]);
	} // If no linkSource input, this process is for collecting link
	// target, where snap should not be accepted.

	if (!noSnap && axisInfo.snap) {
		if (axis.containData(snapToValue) && snapToValue != null) {
			newValue = snapToValue;
		}
	}

	updaters.showPointer(axisInfo, newValue, payloadBatch); // Tooltip should always be snapToValue, otherwise there will be
	// incorrect "axis value ~ series value" mapping displayed in tooltip.

	updaters.showTooltip(axisInfo, payloadInfo, snapToValue);
}

function buildPayloadsBySeries(value, axisInfo) {
	var axis = axisInfo.axis;
	var dim = axis.dim;
	var snapToValue = value;
	var payloadBatch = [];
	var minDist = Number.MAX_VALUE;
	var minDiff = -1;
	each(axisInfo.seriesModels, function (series, idx) {
		var dataDim = series.getData().mapDimensionsAll(dim);
		var seriesNestestValue;
		var dataIndices;

		if (series.getAxisTooltipData) {
			var result = series.getAxisTooltipData(dataDim, value, axis);
			dataIndices = result.dataIndices;
			seriesNestestValue = result.nestestValue;
		} else {
			dataIndices = series.getData().indicesOfNearest(dataDim[0], value, // Add a threshold to avoid find the wrong dataIndex
				// when data length is not same.
				// false,
				axis.type === 'category' ? 0.5 : null);

			if (!dataIndices.length) {
				return;
			}

			seriesNestestValue = series.getData().get(dataDim[0], dataIndices[0]);
		}

		if (seriesNestestValue == null || !isFinite(seriesNestestValue)) {
			return;
		}

		var diff = value - seriesNestestValue;
		var dist = Math.abs(diff); // Consider category case

		if (dist <= minDist) {
			if (dist < minDist || diff >= 0 && minDiff < 0) {
				minDist = dist;
				minDiff = diff;
				snapToValue = seriesNestestValue;
				payloadBatch.length = 0;
			}

			each(dataIndices, function (dataIndex) {
				payloadBatch.push({
					seriesIndex: series.seriesIndex,
					dataIndexInside: dataIndex,
					dataIndex: series.getData().getRawIndex(dataIndex)
				});
			});
		}
	});
	return {
		payloadBatch: payloadBatch,
		snapToValue: snapToValue
	};
}

function showPointer(showValueMap, axisInfo, value, payloadBatch) {
	showValueMap[axisInfo.key] = {
		value: value,
		payloadBatch: payloadBatch
	};
}

function showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) {
	var payloadBatch = payloadInfo.payloadBatch;
	var axis = axisInfo.axis;
	var axisModel = axis.model;
	var axisPointerModel = axisInfo.axisPointerModel; // If no data, do not create anything in dataByCoordSys,
	// whose length will be used to judge whether dispatch action.

	if (!axisInfo.triggerTooltip || !payloadBatch.length) {
		return;
	}

	var coordSysModel = axisInfo.coordSys.model;
	var coordSysKey = modelHelper.makeKey(coordSysModel);
	var coordSysItem = dataByCoordSys.map[coordSysKey];

	if (!coordSysItem) {
		coordSysItem = dataByCoordSys.map[coordSysKey] = {
			coordSysId: coordSysModel.id,
			coordSysIndex: coordSysModel.componentIndex,
			coordSysType: coordSysModel.type,
			coordSysMainType: coordSysModel.mainType,
			dataByAxis: []
		};
		dataByCoordSys.list.push(coordSysItem);
	}

	coordSysItem.dataByAxis.push({
		axisDim: axis.dim,
		axisIndex: axisModel.componentIndex,
		axisType: axisModel.type,
		axisId: axisModel.id,
		value: value,
		// Caustion: viewHelper.getValueLabel is actually on "view stage", which
		// depends that all models have been updated. So it should not be performed
		// here. Considering axisPointerModel used here is volatile, which is hard
		// to be retrieve in TooltipView, we prepare parameters here.
		valueLabelOpt: {
			precision: axisPointerModel.get(['label', 'precision']),
			formatter: axisPointerModel.get(['label', 'formatter'])
		},
		seriesDataIndices: payloadBatch.slice()
	});
}

function updateModelActually(showValueMap, axesInfo, outputPayload) {
	var outputAxesInfo = outputPayload.axesInfo = []; // Basic logic: If no 'show' required, 'hide' this axisPointer.

	each(axesInfo, function (axisInfo, key) {
		var option = axisInfo.axisPointerModel.option;
		var valItem = showValueMap[key];

		if (valItem) {
			!axisInfo.useHandle && (option.status = 'show');
			option.value = valItem.value; // For label formatter param and highlight.

			option.seriesDataIndices = (valItem.payloadBatch || []).slice();
		} // When always show (e.g., handle used), remain
		// original value and status.
		else {
			// If hide, value still need to be set, consider
			// click legend to toggle axis blank.
			!axisInfo.useHandle && (option.status = 'hide');
		} // If status is 'hide', should be no info in payload.

		option.status === 'show' && outputAxesInfo.push({
			axisDim: axisInfo.axis.dim,
			axisIndex: axisInfo.axis.model.componentIndex,
			value: option.value
		});
	});
}

function dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction) {
	// Basic logic: If no showTip required, hideTip will be dispatched.
	if (illegalPoint(point) || !dataByCoordSys.list.length) {
		dispatchAction({
			type: 'hideTip'
		});
		return;
	} // In most case only one axis (or event one series is used). It is
	// convenient to fetch payload.seriesIndex and payload.dataIndex
	// directly. So put the first seriesIndex and dataIndex of the first
	// axis on the payload.

	var sampleItem = ((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {};
	dispatchAction({
		type: 'showTip',
		escapeConnect: true,
		x: point[0],
		y: point[1],
		tooltipOption: payload.tooltipOption,
		position: payload.position,
		dataIndexInside: sampleItem.dataIndexInside,
		dataIndex: sampleItem.dataIndex,
		seriesIndex: sampleItem.seriesIndex,
		dataByCoordSys: dataByCoordSys.list
	});
}

function dispatchHighDownActually(axesInfo, dispatchAction, api) {
	// FIXME
	// highlight status modification should be a stage of main process?
	// (Consider confilct (e.g., legend and axisPointer) and setOption)
	var zr = api.getZr();
	var highDownKey = 'axisPointerLastHighlights';
	var lastHighlights = inner(zr)[highDownKey] || {};
	var newHighlights = inner(zr)[highDownKey] = {}; // Update highlight/downplay status according to axisPointer model.
	// Build hash map and remove duplicate incidentally.

	each(axesInfo, function (axisInfo, key) {
		var option = axisInfo.axisPointerModel.option;
		option.status === 'show' && axisInfo.triggerEmphasis && each(option.seriesDataIndices, function (batchItem) {
			var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex;
			newHighlights[key] = batchItem;
		});
	}); // Diff.

	var toHighlight = [];
	var toDownplay = [];
	each(lastHighlights, function (batchItem, key) {
		!newHighlights[key] && toDownplay.push(batchItem);
	});
	each(newHighlights, function (batchItem, key) {
		!lastHighlights[key] && toHighlight.push(batchItem);
	});
	toDownplay.length && api.dispatchAction({
		type: 'downplay',
		escapeConnect: true,
		// Not blur others when highlight in axisPointer.
		notBlur: true,
		batch: toDownplay
	});
	toHighlight.length && api.dispatchAction({
		type: 'highlight',
		escapeConnect: true,
		// Not blur others when highlight in axisPointer.
		notBlur: true,
		batch: toHighlight
	});
}

function findInputAxisInfo(inputAxesInfo, axisInfo) {
	for (var i = 0; i < (inputAxesInfo || []).length; i++) {
		var inputAxisInfo = inputAxesInfo[i];

		if (axisInfo.axis.dim === inputAxisInfo.axisDim && axisInfo.axis.model.componentIndex === inputAxisInfo.axisIndex) {
			return inputAxisInfo;
		}
	}
}

function makeMapperParam(axisInfo) {
	var axisModel = axisInfo.axis.model;
	var item = {};
	var dim = item.axisDim = axisInfo.axis.dim;
	item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex;
	item.axisName = item[dim + 'AxisName'] = axisModel.name;
	item.axisId = item[dim + 'AxisId'] = axisModel.id;
	return item;
}

function illegalPoint(point) {
	return !point || point[0] == null || isNaN(point[0]) || point[1] == null || isNaN(point[1]);
}